import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:tv_friso_screen/utils/share_pre_utils.dart';
import '../data_model/version_info.dart';
import '../model/response_with_object.dart';
import '../nav_key.dart';
import '../utils/loading_utils.dart';
import '../utils/log_utils.dart';
import '../utils/result_check_utils.dart';
import '../utils/toast_utils.dart';
import 'address.dart';

class HttpManager {
  /* 10000 => '成功！' (唯一成功code)
  20111 => '系统服务端异常！'
  20001 => '请求参数异常！'
  20002 => string '请求路径非法！'
  20003 => string '服务端请求access_token令牌缺失！' (需要重新登陆)
  20004 => string '账号已被其他设备占用，请重新登录！' (需要重新登陆)
  20005 => string '请求方式非法！' (GET 或 POST)
  20006 => string '请求提交参数验证不通过！' ()
  20007 => string '账户状态异常！' ()
  20008 => string '账户未进行微信授权！' ()
  20009 => string '您的请求太频繁了，请稍后再试！' ()
  20888 => string '您的账户存在违规操作，无法继续访问！' ()
  20555 => string '系统维护升级中，请稍后再试！' ()
  21000 => string '门店待审核！'
  21001 => string '门店审核失败！'
  21002 => string '门店审核失败！'
  22001 => string '非门店端人员！
  20020 => string '非最新版本！'*/

  static const codeSuccess = 10000;

  //版本更新
  static const codeUpdate = 20008;

  //报错需回到登录页
  static const codeError = 20003;

  static const codeError2 = 20004;

  static Map<String, dynamic> httpHeaders = {
    'Accept': 'application/json,*/*',
    'Content-Type': 'application/json',
    'x-platform-code': '0001'
  };

  static BaseOptions baseOptions = BaseOptions(
    connectTimeout: const Duration(seconds: 150000),
    receiveTimeout: const Duration(seconds: 150000),
    responseType: ResponseType.json,
    validateStatus: (status) {
      return true;
    },
    baseUrl: Address.baseUrl,
    headers: httpHeaders,
  );

  Dio createInstance() {
    Dio dio = Dio(baseOptions);

    (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
        (client) {
      client.badCertificateCallback = (cert, host, port) {
        return true;
      };
    };

    dio.interceptors.add(
      //拦截器
      InterceptorsWrapper(
        onRequest: (options, handler) {
          // 如果你想完成请求并返回一些自定义数据，你可以使用 `handler.resolve(response)`。
          // 如果你想终止请求并触发一个错误，你可以使用 `handler.reject(error)`。

          String requestStr =
              "\n==================== REQUEST ====================\n"
              "- URL:\n${options.baseUrl + options.path}\n"
              "- METHOD: ${options.method}\n"
              "- HEADER:\n${options.headers}\n"
              "- QUERY PARAMETERS:\n${options.queryParameters}\n"
              "- DATA:\n${options.data}\n";

          final data = options.data;

          if (data != null) {
            if (data is Map) {
              requestStr += "- data is Map DATA:\n$data\n";
            } else if (data is FormData) {
              final formDataMap = {}
                ..addEntries(data.fields)
                ..addEntries(data.files);
              requestStr += "- data is FormData DATA:\n$formDataMap\n";
            } else {
              requestStr += "- DATA:\n$data\n";
            }
          }

          LogUtils.showLog(requestStr);

          return handler.next(options);
        },
        onResponse: (response, handler) {
          // 如果你想终止请求并触发一个错误，你可以使用 `handler.reject(error)`。

          String responseStr =
              "\n==================== RESPONSE ====================\n"
              "- RESPONSE:\n$response\n";

          responseStr += "- HEADER:\n{";
          response.headers.forEach(
              (key, list) => responseStr += "\n  " "\"$key\" : \"$list\",");
          responseStr += "\n}\n";

          responseStr += "- STATUS: ${response.statusCode}\n";

          if (response.data != null) {
            responseStr += "- BODY:\n ${_parseResponse(response)}";
          }

          printWrapped(responseStr);

          return handler.next(response);
        },
        onError: (err, handler) {
          // 如果你想完成请求并返回一些自定义数据，你可以使用 `handler.resolve(response)`。

          String errorStr =
              "\n==================== ERROR ====================\n"
              "- ERROR:\n$err\n";

          if (err.response != null && err.response?.data != null) {
            LogUtils.showLog('╔ ${err.type.toString()}');
            errorStr += "- ERROR:\n${_parseResponse(err.response?.data)}\n";
          } else {
            errorStr += "- ERRORTYPE: ${err.type}\n";
            errorStr += "- MSG: ${err.message}\n";
          }

          LogUtils.showLog(errorStr);

          if (errorStr.contains("SocketException")) {
            ToastUtils.showToast("网络不佳，或没有网络");
          }

          return handler.next(err);
        },
      ),
    );

    // dio.interceptors.add(CustomInterceptors());

    return dio;
  }

  ///通用的GET请求
  get(api, {query, withLoading = true, checkUpdate = true}) async {
    if (withLoading) {
      LoadingUtils.show();
    }

    Response response;

    try {
      response = await createInstance().get(api, queryParameters: query);
    } on DioException catch (e) {
      throw Exception(e);
    } finally {
      if (withLoading) {
        LoadingUtils.dismiss();
      }
    }

    var code = response.data['code'];
    if (code == codeError || code == codeError2) {
      //回到登录页
      SharePreUtils.setToken("");
      ResultCheckUtils.goLoginPage(NavKey.navKey.currentContext!);
    }
    //版本更新
    else if (checkUpdate && code == codeUpdate) {
      ResultCheckUtils.postLoginAndUpdate(NavKey.navKey.currentContext!);
    }
    return response.data;
  }

  ///通用的POST请求
  post(api, {query, params, withLoading = true, checkUpdate = true}) async {
    if (withLoading) {
      LoadingUtils.show();
    }
    Response response;

    try {
      response = await createInstance()
          .post(api, queryParameters: query, data: params);
    } on DioException catch (e) {
      throw Exception(e);
    } finally {
      if (withLoading) {
        LoadingUtils.dismiss();
      }
    }

    var code = response.data['code'];
    if (code == codeError || code == codeError2) {
      //回到登录页
      SharePreUtils.setToken("");
      ResultCheckUtils.goLoginPage(NavKey.navKey.currentContext!);
    }
    //版本更新
    else if (checkUpdate && code == codeUpdate) {
      ResultCheckUtils.postLoginAndUpdate(NavKey.navKey.currentContext!);
    }
    return response.data;
  }
}

String _parseResponse(Response response) {
  String responseStr = "";
  var data = response.data;
  if (data is Map) {
    responseStr += data.mapToStructureString();
  } else if (data is List) {
    responseStr += data.listToStructureString();
  } else {
    responseStr += response.data.toString();
  }

  return responseStr;
}

void printWrapped(String text) {
  final pattern = RegExp('.{1,800}'); // 800 is the size of each chunk

  pattern.allMatches(text).forEach((match) => LogUtils.showLog(match.group(0)));
}

extension Map2StringEx on Map {
  String mapToStructureString({int indentation = 2}) {
    String result = "";
    String indentationStr = " " * indentation;
    if (true) {
      result += "{";
      forEach((key, value) {
        if (value is Map) {
          var temp = value.mapToStructureString(indentation: indentation + 2);
          result += "\n$indentationStr" "\"$key\" : $temp,";
        } else if (value is List) {
          result += "\n$indentationStr"
              "\"$key\" : ${value.listToStructureString(indentation: indentation + 2)},";
        } else {
          result += "\n$indentationStr" "\"$key\" : \"$value\",";
        }
      });
      result = result.substring(0, result.length - 1);
      result += indentation == 2 ? "\n}" : "\n${" " * (indentation - 1)}}";
    }

    return result;
  }
}

extension List2StringEx on List {
  String listToStructureString({int indentation = 2}) {
    String result = "";
    String indentationStr = " " * indentation;
    if (true) {
      result += "$indentationStr[";
      forEach((value) {
        if (value is Map) {
          var temp = value.mapToStructureString(indentation: indentation + 2);
          result += "\n$indentationStr" "\"$temp\",";
        } else if (value is List) {
          result += value.listToStructureString(indentation: indentation + 2);
        } else {
          result += "\n$indentationStr" "\"$value\",";
        }
      });
      result = result.substring(0, result.length - 1);
      result += "\n$indentationStr]";
    }

    return result;
  }
}
